package com.snap.awesomeserial.serial;

import com.snap.awesomeserial.bean.NAAction;

import java.util.ArrayList;
import java.util.List;

import static com.snap.awesomeserial.AppConfig.HOLE_ANGLE;

/**
 * cmd[2] 设备号
 * cmd[3] cmd[4] 帧长
 * cmd[5] 帧类型
 * cmd[6] 功能码
 * <p>
 * 最短8个字节（假设只有功能码 不含数据）
 */
public class Cmd {

    public static final byte CMD_START1 = (byte) 0xa5;
    public static final byte CMD_START2 = (byte) 0x5a;

    /**
     * 验证命令是否符合协议
     */
    public static boolean cmdValidate(byte[] cmd) {
        if (cmd == null) return false;
        if (cmd.length < 8) return false;
        if (cmd[0] != CMD_START1) return false;
        if (cmd[1] != CMD_START2) return false;
        int frameLength = ((cmd[3] & 0xff) << 8) + (cmd[4] & 0xff);
        if (frameLength != cmd.length - 2) return false;
        return cmd[cmd.length - 1] == getParity(cmd);
    }

    /**
     * 是否包含大于1条指令
     */
    public static boolean isOneMore(byte[] cmd) {
        return cmd != null
                && cmd.length > 8
                && cmd[0] == CMD_START1
                && cmd[1] == CMD_START2
                && cmd.length > ((cmd[3] & 0xff) << 8) + (cmd[4] & 0xff) + 2;
    }

    private static byte[] fill(int deviceId, byte[] data) {
        byte[] cmd = new byte[data.length + 6];
        cmd[0] = (byte) 0xa5;
        cmd[1] = (byte) 0x5a;
        cmd[2] = (byte) deviceId;
        int length = cmd.length - 2;
        cmd[3] = (byte) ((length & 0xff00) >> 8);
        cmd[4] = (byte) (length & 0xff);
        System.arraycopy(data, 0, cmd, 5, data.length);
        cmd[cmd.length - 1] = getParity(cmd);
        return cmd;
    }

    /**
     * 获取校验位
     */
    private static byte getParity(byte[] cmd) {
        byte parity = 0x00;
        for (int i = 0; i < cmd.length - 1; i++) {
            parity += (cmd[i] & 0xff);
        }
        return parity;
    }



    public static byte[] pcrOperation(int deviceId, boolean start) {
        byte[] data = new byte[3];
        data[0] = 0;//控制帧0
        data[1] = 1;//功能码
        data[2] = (byte) (start ? 0 : 1);
        return fill(deviceId, data);
    }

    public static byte[] pid(int deviceId, float[] params) {
        if (params.length != 12) {
            throw new RuntimeException();
        }
        byte[] data = new byte[26];
        data[0] = 0;//控制帧0
        data[1] = 2;//功能码
        for (int i = 0; i < params.length; i++) {
            int value = (int) (params[i] * 100);
            data[i * 2 + 2] = (byte) ((value & 0xff00) >> 8);
            data[i * 2 + 3] = (byte) (value & 0xff);
        }
        return fill(deviceId, data);
    }

    public static byte[] tempDrift(int deviceId, float[] params) {
        if (params.length != 4) {
            throw new RuntimeException();
        }
        byte[] data = new byte[10];
        data[0] = 0;//控制帧0
        data[1] = 3;//功能码
        for (int i = 0; i < params.length; i++) {
            int value = (int) (params[i] * 100);
            data[i * 2 + 2] = (byte) ((value & 0xff00) >> 8);
            data[i * 2 + 3] = (byte) (value & 0xff);
        }
        return fill(deviceId, data);
    }



    private static List<Byte> naAction(NAAction action) {
        List<Byte> byteList = new ArrayList<>();
        if (action.getType() == 0) {//等待
            byteList.add((byte) 2);
            byteList.add((byte) ((action.getDelay() & 0xff00) >> 8));
            byteList.add((byte) (action.getDelay() & 0xff));
            byteList.add((byte) 0);
            byteList.add((byte) 0);
        } else if (action.getType() == 1 || action.getType() == 2) {//抽取、导出
            //1.水平动作
            byteList.add((byte) 0);
            byteList.add((byte) 1);//0顺1逆
            byteList.add((byte) 60);//水平固定是60rpm
            float destAngle = HOLE_ANGLE[action.getPort()][action.getRoomNo()];
            int angle = (int) (destAngle * 100);
            byteList.add((byte) ((angle & 0xff00) >> 8));
            byteList.add((byte) (angle & 0xff));
            //2.垂直动作
            byteList.add((byte) 1);
            byteList.add((byte) (action.getType() - 1));//0升1降 = 0抽1导
            int velocity;
            switch (action.getVelocity()) {
                case 0:
                    velocity = 240;
                    break;
                case 1:
                    velocity = 120;
                    break;
                default:
                    velocity = 60;
            }
            byteList.add((byte) velocity);
            int capacity = action.getCapacity();
            int distance = 51 * capacity;
            byteList.add((byte) ((distance & 0xff00) >> 8));
            byteList.add((byte) (distance & 0xff));
        }
        return byteList;
    }

    public static byte[] naOperation(int deviceId, boolean start) {
        byte[] data = new byte[3];
        data[0] = 0;//控制帧0
        data[1] = 0x31;//功能码
        data[2] = (byte) (start ? 0 : 1);
        return fill(deviceId, data);
    }

    public static byte[] naInit(int deviceId) {
        byte[] data = new byte[3];
        data[0] = 0;//帧类型，控制帧
        data[1] = 0x32;//功能码
        data[2] = 0;//数据
        return fill(deviceId, data);
    }


    public static byte[] naReset(int deviceId) {
        byte[] data = new byte[2];
        data[0] = 0;//控制帧0
        data[1] = 0x33;//功能码
        return fill(deviceId, data);
    }

    /**
     * A5 5A 00 帧长 00(帧类型) 30(功能码) 01(步骤数) 01(动作数) 01(循环数) 00(水平标志位) 01(逆时针) 00 10(速率)  00 05(步数) 校验
     * @param deviceId
     * @param distance
     * @param speed
     * @return
     */
    public static byte[] testX(int deviceId,int direction, int distance, int speed) {
        byte[] data = new byte[11];
        data[0] = 0;//帧类型，控制帧
        data[1] = 0x30;//功能码
        data[2] = 0x01;//步骤数
        data[3] = 0x01;//动作数
        data[4] = 0x01;//循环数
        data[5] = 0x00;//水平标志位
        data[6] = (byte) direction;//逆时针
        data[7] = (byte) (speed & 0xff);//速率
        data[8] =(byte) ((speed & 0xFF00) >> 8);//速率

        data[9] = (byte) (distance & 0xff);//步数
        data[10] = (byte) ((distance & 0xFF00) >> 8);//步数
        return fill(deviceId, data);
    }
    /**
     * A5 5A 00 帧长 00(帧类型) 30(功能码) 01(步骤数) 01(动作数) 01(循环数) 01(垂直标志位) 01(降) 00 10(速率)  00 03(步数) 校验
     * @param deviceId
     * @param distance
     * @param speed
     * @return
     */
    public static byte[] texY(int deviceId, int direction,int distance, int speed) {
        byte[] data = new byte[11];
        data[0] = 0;//帧类型，控制帧
        data[1] = 0x30;//功能码
        data[2] = 0x01;//步骤数
        data[3] = 0x01;//动作数
        data[4] = 0x01;//循环数
        data[5] = 0x01;//垂直标志位
        data[6] = (byte) direction;//降
        data[7] = (byte) (speed & 0xff);//速率
        data[8] =(byte) ((speed & 0xFF00) >> 8);//速率

        data[9] = (byte) (distance & 0xff);//步数
        data[10] = (byte) ((distance & 0xFF00) >> 8);//步数
        return fill(deviceId, data);
    }
    public static byte[] testZ(int deviceId,int direction, int distance, int speed) {
        byte[] data = new byte[11];
        data[0] = 0;//帧类型，控制帧
        data[1] = 0x30;//功能码
        data[2] = 0x01;//步骤数
        data[3] = 0x01;//动作数
        data[4] = 0x01;//循环数
        data[5] = 0x02;//内部垂直标志位
        data[6] = (byte) direction;//降
        data[7] = (byte) (speed & 0xff);//速率
        data[8] =(byte) ((speed & 0xFF00) >> 8);//速率

        data[9] = (byte) (distance & 0xff);//步数
        data[10] = (byte) ((distance & 0xFF00) >> 8);//步数
        return fill(deviceId, data);
    }

    /**
     * 开0x00
     * 关0x01
     */
    public static byte[] controlLed(int deviceId, int value) {
        byte[] data = new byte[3];
        data[0] = 0;//帧类型，控制帧
        data[1] = 0x33;//功能码
        data[2] = (byte) value;//
        return fill(deviceId, data);
    }


    /**
     * 开0x00
     * 关0x01
     */
    public static byte[] controlFan(int deviceId, int value) {
        byte[] data = new byte[3];
        data[0] = 0;//帧类型，控制帧
        data[1] = 0x34;//功能码
        data[2] = (byte) value;//步骤数
        return fill(deviceId, data);
    }

    /**
     * 开0x00
     * 关0x01
     */
    public static byte[] controlUV(int deviceId, int value) {
        byte[] data = new byte[3];
        data[0] = 0;//帧类型，控制帧
        data[1] = 0x35;//功能码
        data[2] = (byte) value;//步骤数
        return fill(deviceId, data);
    }

    /**
     *  查询舱门是否完全关闭
     *  帧头(0xA5 0x5A)  + 设备地址 + 帧长(2byte) + 帧类型(0x02) + 查询码(1byte) + 数据信息(n bytes) + 校验(1 byte)
     */
    public static byte[] queryDoor(int deviceId) {
        byte[] data = new byte[2];
        data[0] = 02;//帧类型，控制帧
        data[1] = 0x33;//功能码
        return fill(deviceId, data);
    }
    /**
     * 帧头(0xA5 0x5A)  + 设备地址 + 帧长(2byte) + 帧类型(0x02) + 查询码(1byte) + 数据信息(n bytes) + 校验(1 byte)
     */
    public static byte[] queryTemp(int deviceId) {
        byte[] data = new byte[2];
        data[0] = 02;//帧类型，查询帧
        data[1] = 0x01;//查询码
        return fill(deviceId, data);
    }

    /**
     * A5 5A``01``00 08``00`36`0C B7`
     * int temp = 5000;
     * byte low = (byte) (temp & 0xff);
     * byte high = (byte) ((temp & 0xFF00) >> 8);
     * @param deviceId
     * @param value
     * @return
     */
    public static byte[] setTemp(int deviceId, float value) {
        int temp= (int) (value*100);
        byte[] data = new byte[4];
        data[0] = 00;//帧类型，
        data[1] = 0x36;//查询码
        data[2] =  (byte) (temp & 0xff);//查询码
        data[3] = (byte) ((temp & 0xFF00) >> 8);//查询码
        return fill(deviceId, data);
    }
    public static byte[] query(int deviceId, int... type) {
        if (type.length == 0) {
            throw new RuntimeException();
        } else if (type.length == 1) {
            byte[] data = new byte[2];
            data[0] = 2;
            data[1] = (byte) type[0];
            return fill(deviceId, data);
        } else {
            byte[] data = new byte[type.length + 3];
            data[0] = 2;
            data[1] = (byte) 0xfe;
            data[2] = (byte) type.length;
            for (int i = 0; i < type.length; i++) {
                data[i + 3] = (byte) type[i];
            }
            return fill(deviceId, data);
        }
    }
}